import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpRequest } from '@angular/common/http';
import { Events, Platform } from 'ionic-angular';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { forkJoin } from 'rxjs/observable/forkJoin';

declare var $: any;
import * as _ from 'lodash';

@Injectable()
export class RestService {
  // private hostUrl = 'https://120.27.13.7';192.168.123.235
  private hostUrl = 'https://47.105.79.216';
  private tokenUrl  = 'https://47.105.79.216';
  private token = '';
  private refreshToken = '';
  private tokenInterval;

  debugging = false;
  doLoginFlag = true;

  constructor(private http: HttpClient,
              private platform: Platform,
              private events: Events) {
    if (this.debugging) {
      this.hostUrl = 'assets/samples/';
    }

    this.platform.ready().then(() => {
      this.startRefreshToken();
    });
  }

  startRefreshToken() {
    if (this.debugging || !this.doLoginFlag) {
      return;
    }

    if (this.tokenInterval) {
      clearInterval(this.tokenInterval);
    }

    this.tokenInterval = setInterval(() => {
      if (!this.debugging && this.doLoginFlag) {
        this.refreshAccessToken();
      }
    }, 240000);
  }

  stopRefreshToken() {
    if (this.tokenInterval) {
      clearInterval(this.tokenInterval);
    }
  }

  listValidRoles() {
    return ['admin', 'director', 'doctor', 'nurse', 'header'];
  }

  refreshAccessToken() {
    this.refreshTokenFromServer(data => {
      this.token = data['access_token'];
      this.refreshToken = data['refresh_token'];
      this.saveToken(this.token);
    }, () => {
      this.reload2Login();
    });
  }

  private attachAuthField(headers) {
    if (this.doLoginFlag) {
      if (!this.token) {
        this.token = this.getToken();
      }

      if (this.token) {
        headers['Authorization'] = 'Bearer ' + this.token;
      } else {
        this.reload2Login();
      }
    }
  }

  private getCommonHeaders() {
    const headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'Content-Type': 'application/json'
     // 'Access-Control-Allow-Origin': '*',
     // 'Access-Control-Allow-Methods': 'GET,POST,OPTIONS,DELETE,PUT'
    };

    this.attachAuthField(headers);

    return new HttpHeaders(headers);
  }

  private getUploadHeaders() {
    const headers = {
      'X-Requested-With': 'XMLHttpRequest',
      // 'Content-Type': 'multipart/form-data'
    };

    this.attachAuthField(headers);

    return new HttpHeaders(headers);
  }

  public getRaw(suffix: string) {
    return this.http.get(this.hostUrl + suffix, {
      headers: this.getCommonHeaders(),
      responseType: 'text'
    });
  }

  public getLocalFile(images: any[]) {
    const observableBatch = [];
    images.forEach(image => {
      observableBatch.push(this.http.get(image.src, {
        responseType: 'blob'
      }));
    })

    return forkJoin(observableBatch);
  }

  public get(suffix: string) {
    let myHeaders = this.getCommonHeaders();

    return this.http.get(this.hostUrl + suffix, {
      headers: myHeaders
    })
    .catch(this.handleDetailedError);
  }

  public getWithParams(suffix: string, params) {
    return this.http.get(this.hostUrl + suffix, {
      headers: this.getCommonHeaders(),
      params: params
    })
    .catch(this.handleDetailedError);
  }

  public post(suffix: string, body: any) {
    return this.http.post(this.hostUrl + suffix, body, {
      headers: this.getUploadHeaders()
    })
    .catch(this.handleDetailedError);
  }

  public upload(files: File[], message: string, phone: string, instition: string) {
    const formdata = new FormData();
    files.forEach(file => {
      formdata.append('file', file);
    });

    formdata.append('message', message);
    formdata.append('phone', phone);
    formdata.append('institution', instition);

    const req = new HttpRequest('POST', this.hostUrl + '/api/upload', formdata, {
      headers: this.getUploadHeaders(),
      reportProgress: false,
      responseType: 'text'
    });

    return this.http.request(req).catch(this.handleDetailedError);
  }

  public getTokenFromServer(parameters, success, fail) {
    this.http.post(this.tokenUrl + ':8443/auth/realms/hushitong/protocol/openid-connect/token', $.param(parameters), {
      headers: new HttpHeaders({'Content-Type': 'application/x-www-form-urlencoded'})
    }).subscribe(response => {
      if (success) {
        success(response);
      }
    }, error => {
      console.error('Failed to get token from server.', JSON.stringify(error));
      if (fail) {
        fail();
      }
    });
  }

  public refreshTokenFromServer(success, fail) {
    if (!this.refreshToken) {
      console.warn('There is no refresh token available now, skip to refresh token.');
      return;
    }

    const params = {
      grant_type: 'refresh_token',
      refresh_token: this.refreshToken,
      client_id: 'webmobile',
    };

    this.http.post(this.tokenUrl + ':8443/auth/realms/hushitong/protocol/openid-connect/token', $.param(params), {
      headers: new HttpHeaders({'Content-Type': 'application/x-www-form-urlencoded'})
    }).subscribe(response => {
      if (success) {
        success(response);
      }
    }, error => {
      console.error('Failed to refresh token from server.', error);
      if (fail) {
        fail();
      }
    });
  }

  public put(suffix: string, body: any) {
    return this.http.put(this.hostUrl + suffix, body, {
      headers: this.getCommonHeaders()
    });
  }

  public patch(suffix: string, body: any) {
    return this.http.patch(this.hostUrl + suffix, body, {
      headers: this.getCommonHeaders()
    });
  }

  public delete(suffix: string) {
    return this.http.delete(this.hostUrl + suffix, {
      headers: this.getCommonHeaders()
    });
  }

  public deleteAll(urls: string[]) {
    const observableBatch = [];
    urls.forEach(url => {
      observableBatch.push(this.delete(url));
    })

    return forkJoin(observableBatch);
  }

  /**
   * This is to return detailed error reponse.
   */
  public handleDetailedError = (error: HttpErrorResponse | any) => {
    let body: {};
    if (error instanceof HttpErrorResponse) {
      if (error.status === 401 || error.status === 403) {
        this.reload2Login();
      } else {
        const contentType = error.headers.get('content-type');
        if (contentType && (contentType.indexOf('application/json') !== -1)) {
          body = error || {};
        } else {
          body = error;
        }
        return Observable.throw(body);
      }
    } else {
      body = error;
      return Observable.throw(body);
    }
  }

  public reload2Login() {
    if (this.doLoginFlag) {
      this.stopRefreshToken();
      this.events.publish('token:failed');
    }
  }

  saveToken(token) {
    this.token = token;
    localStorage.setItem('token', this.token);
  }

  removeToken() {
    this.token = null;
    localStorage.removeItem('token');
  }

  getToken() {
    const item = localStorage.getItem('token');
    return item ? item : '';
  }

  saveRefreshToken(token) {
    this.refreshToken = token;
  }

  removeRefreshToken() {
    this.refreshToken = null;
  }

  getRefreshToken() {
    return this.refreshToken;
  }

  clearup() {
    this.removeToken();
    this.removeRefreshToken();

    // clean up user password
    const user = this.getUser();
    if (user && user.password) {
      user.password = '';
    }

    this.saveUser(user);
  }

  saveUser(user) {
    localStorage.setItem('user', JSON.stringify(user));
  }

  getUser() {
    const item = JSON.parse(localStorage.getItem('user'));
    return item ? item : {};
  }

  saveFilter(filter) {
    localStorage.setItem('filter', JSON.stringify(filter));
  }

  getFilter() {
    const item = JSON.parse(localStorage.getItem('filter'));
    return item ? item : {
      persons: [],
      nurses: [],
      buildings: [],
      levels: [],
      rooms: []
    };
  }

  saveLanguage(langugage) {
    localStorage.setItem('language', langugage);
  }

  getLanguage() {
    return localStorage.getItem('language');
  }

  getVersion() {
    return 'V1.0.0';
  }

  /**
   * Update "sources" with new input "updates" with common key "id".
   */
  updateArray(sources, updates) {
    // Remove un-qualified source
    _.remove(sources, source => {
      return _.findIndex(updates, {id: source.id}) === -1;
    });

    updates.forEach((update, index) => {
      const oldIndex = _.findIndex(sources, {id: update.id});
      if (oldIndex === -1) {
        sources.splice(index, 0, update);
      } else {
        // update the existing old with new values
        _.assign(sources[oldIndex], update);
      }
    });
  }

  getAlarmEnumNumber(alarm) {
    switch(alarm) {
    case 'HEART':
      return 0;
    case 'BREATHE':
      return 1;
    case 'UP':
      return 2;
    case 'SIDE':
      return 3;
    case 'AWAY':
      return 4;
    case 'WET':
      return 5;
    case 'MOVE':
      return 6;
    case 'DEVICE':
      return 7;
    case 'RING':
      return 8;
    case 'LEFT':
      return 9;
    case 'RIGHT':
      return 10;
    case 'HIST_HEART':
      return 11;
    case 'HIST_BREATHE':
      return 12;
    case 'HIST_MOVE':
      return 13;
    case 'SLEEP':
      return 14;
    case 'TURN_OVER':
      return 15;
    }

    return -1;
  }
}
